#pragma once
#include "system/atomic.h"

namespace gecko
{

	//---------------------------------------------------------------------------------------------

	// forward declare types
	template<typename T> class StrongPtr;
	template<typename T> class WeakPtr;
	namespace internal { template<typename T> class RefCount; }

	//---------------------------------------------------------------------------------------------

	// strong pointer
	template<typename T>
	class StrongPtr
	{
	public:
		// default constructor
		StrongPtr();

		// construct from raw pointer
		StrongPtr(T * ptr);

		// cleanup references
		~StrongPtr();

		// construct from strong pointer
		StrongPtr(const StrongPtr & rhs);

		// construct from weak pointer
		StrongPtr(const WeakPtr<T> & rhs);

		// construct from other type of strong pointer
		template<typename Ty> StrongPtr(const StrongPtr<Ty> & rhs);

		// assignment operator
		void operator =(const StrongPtr & rhs);

		// assignment operator
		template<typename Ty> void operator =(const StrongPtr<Ty> & rhs);

		// conditional operator
		operator bool();

		// dereference operator
		T * operator -> ();

		// const dereference operator
		const T * operator -> () const;

		// dereference operator
		T & operator * ();

		// const dereference operator
		const T & operator * () const;

		internal::RefCount<T> * m_refCount;
		template<typename T> friend class WeakPtr;
	};

	//---------------------------------------------------------------------------------------------

	// weak pointer
	template<typename T>
	class WeakPtr
	{
	public:
		// default constructor
		WeakPtr();

		// construct from a strong pointer
		WeakPtr(const StrongPtr<T> & ptr);

		// clean up references
		~WeakPtr();

		// assign a strong pointer
		void operator = (const StrongPtr<T> & ptr);

		// check the pointer is still available
		bool IsValid();

		// convert the weak pointer to a strong in order to access/dereference
		StrongPtr<T> Lock();

	private:
		internal::RefCount<T> * m_refCount;
		template<typename T> friend class StrongPtr;
	};

	//---------------------------------------------------------------------------------------------

}

#include "smartptr.inl"
